#include <assert.h>

#include "list.h"
#include "dynamicarray.h"
#include "threadpool.h"
#include "server.h"
#include "inputselector.h"

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

#undef max
#define max(x,y) ((x) > (y) ? (x) : (y))

InputSelector inputSelector;


struct s_server_serveClient_eventArgs
{
    Server* server;
    int* client;
} typedef server_serveClient_eventArgs;

/**
 * @brief server_serveClient_handler
 * @param args convertie les argument générique en argument de la fonction
 * server_serveClient à appeler
 * @return
 */
void * server_serveClient_handler ( void * args);

/**
 * @brief run boucle principale du programme
 * @param server server sur lequel écouté les connexions entrante
 * et les requêtes clients
 * @param threadpool pool de thread sur lequel lancer les opérations de traitement
 * des sockets clients
 */
void run(Server* server, ThreadPool* threadpool);

int main(int argc, char ** argv)
{
    if(argc == 3)
    {
        Server server;
        if(server_initialize(&server,argv[2]))
        {
            ThreadPool threadpool;
            threadpool_initialize(&threadpool,atoi(argv[1]));

            run(&server,&threadpool);
            inputSelector_clear(&inputSelector);
            server_clear(&server);
            threadpool_clear(&threadpool);
        }
    }
    else
    {
        fprintf(stderr,"Nombre d'arguments incorecte");
    }
    return 0;
}

void * server_serveClient_handler ( void * args)
{
    server_serveClient_eventArgs* eventArgs= (server_serveClient_eventArgs*)args;
    if (server_serveClient(eventArgs->server, eventArgs->client))
    {
        inputSelector_add(&inputSelector, *((int*) eventArgs->client));
    }
    return NULL;
}

void run(Server* server, ThreadPool* threadpool)
{
    int fd;
    int loop = 1;

    inputSelector_initialize(&inputSelector);
    inputSelector_add(&inputSelector,0);
    inputSelector_add(&inputSelector,server->socket);

    while(loop)
    {
        if(inputSelector_selectAndRemove(&inputSelector,&fd))
        {
            if(fd != -1)
            {
                if(fd == 0)
                {
                    printf("echo");
                    inputSelector_add(&inputSelector,0);
                }
                /*Sinon si il y a des données en attente sur le socket d'écoute des clients du serveur*/
                else if(fd == server->socket)
                {
                    /*On écoute les données reçue et on tente d'ajouter un nouveau client*/
                    int client = server_accept(server);
                    if(client != -1)
                        inputSelector_add(&inputSelector,client);
                    inputSelector_add(&inputSelector,server->socket);
                }
                /*Sinon c'est un des socket client qui contient des données en attente lecture*/
                else
                {
                    int* client = server_getClientAddress(server,fd);
                    if(client != NULL)
                    {
                        /*Si oui, on lance le traitement de ces données de manière asynchrone*/
                        server_serveClient_eventArgs* e;
                        Delegate* delegate;
                        /*On crée la structure contenant les arguments*/
                        e = (server_serveClient_eventArgs*) malloc(sizeof(server_serveClient_eventArgs));
                        e->server = server;
                        e->client = client;
                        /*On crée le délégué*/
                        delegate_create(&delegate, server_serveClient_handler, (void**)&e);
                        /*On ajoute le délégué à la file d'execution du pool de thread*/
                        threadpool_execute(threadpool,&delegate);
                    }
                }
            }
        }
        /*Sinon il y a eu une erreur*/
        else
        {
            /*On l'indique puis on quitte la boucle de traitement*/
            fprintf(stderr,"Erreur lors du select()\n");
            loop = 0;
        }
    }
}
